package nborm;

import android.annotation.SuppressLint;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

import android.text.TextUtils;
import android.util.Log;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import dalvik.system.BaseDexClassLoader;
import dalvik.system.DexFile;

/**
 * 数据库单例
 */
public class DBHelper extends SQLiteOpenHelper {
    private static final String TAG = "DBHelper";
    private static DBHelper sC;

    //    private String mEntityPath;
    public static DBHelper get() {
        return sC;
    }

    private Class<?>[] mEntities;

    private InitCallback callback;

    public synchronized static void init(Context context, String dbName, int version, Class<?>... entities) {
        sC = new DBHelper(context, dbName, version);
        sC.mEntities = entities;
    }

    public DBHelper(Context context, String dbName, int version) {
        super(context, dbName, null, version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {

    }

    /**
     * @param db
     * @param oldVersion
     * @param newVersion
     */
    @SuppressLint("StaticFieldLeak")
    @Override
    public void onUpgrade(final SQLiteDatabase db, int oldVersion, int newVersion) {
        Log.d(TAG, "onUpgrade:" + oldVersion + "," + newVersion);
        //全自动处理数据库升级．
        Map<String, TableInfo> tableInfoMap = new HashMap<>();
        for (Class<?> table : mEntities) {
            //获取包下所有的实体类型，和数据数据库比对
            TableInfo tableInfo = TableHelper.getTableInfo(table);
            tableInfoMap.put(tableInfo.getTableName(), tableInfo);
            if (!tableInfo.isTableExists(db)) {
                Log.d(TAG, tableInfo.getTableName() + " table not exists,will be created");
                db.execSQL(tableInfo.getCreateTableSql());
            }
        }
        // 实体被删除的情况暂不处理。
        //处理表字段被修改的情况．
        for (Map.Entry<String, TableInfo> entry : tableInfoMap.entrySet()) {

            TableInfo tableInfo = entry.getValue();
            String tableName = tableInfo.getTableName();

            //未修改的字段列表
            List<String> noChangedColumn = getNoChangedColumn(db, entry.getValue());

            //如果未修改的字段列表长度和原字段数量不同，说明字段有修改。
            if (noChangedColumn.size() != tableInfo.getColumnList().size()) {

                Log.d(TAG, tableName + " table has changed");
                //创建临时表并复制旧表
                db.execSQL("create table if not exists tempTable as select * from " + tableName);
                //删除旧表。
                db.execSQL("DROP TABLE " + tableName);
                //创建新表。
                db.execSQL(entry.getValue().getCreateTableSql());

                //示例：insert into newTable (hehe1,hehe2)  select hehe1,hehe2 from oldTable
                String columns = TextUtils.join(",", noChangedColumn);
                db.execSQL("insert into " + tableName + " ( " + columns + " )" +
                        "  select " + columns + " from tempTable ");

                //删除临时表。
                db.execSQL("DROP TABLE tempTable");
            }
        }
    }

    public synchronized SQLiteDatabase getDb() {
        return getWritableDatabase();
    }

    /**
     * 全局的类型转换器。
     * 默认使用Gson处理数据转换。
     */
    private TypeConverter mTypeConverter;

    /**
     * 设置类型转换器。
     *
     * @param typeConverter
     * @param <T>
     */
    public <T> void setTypeConverter(TypeConverter<T> typeConverter) {
        mTypeConverter = typeConverter;
    }

    public <T> TypeConverter<T> getTypeConverter() {
        return mTypeConverter;
    }


    /**
     * 获取所有dex文件
     *
     * @return
     */
    public static List<DexFile> getAllDexFiles() {
        List<DexFile> dexFiles = new ArrayList<>();
        try {
            BaseDexClassLoader baseDexClassLoader = (BaseDexClassLoader) DBHelper.class.getClassLoader();
            Field field = BaseDexClassLoader.class.getDeclaredField("pathList");
            field.setAccessible(true);
            Object dexPathList = field.get(baseDexClassLoader);
            Field f2 = dexPathList.getClass().getDeclaredField("dexElements");
            f2.setAccessible(true);
            Object[] objects = (Object[]) f2.get(dexPathList);
            Field fTemp = null;
            for (Object object : objects) {
                if (fTemp == null) {
                    fTemp = object.getClass().getDeclaredField("dexFile");
                }
                fTemp.setAccessible(true);
                Object dex = fTemp.get(object);
                if (dex instanceof DexFile) {
                    dexFiles.add((DexFile) dex);
                }
            }
        } catch (Exception e) {
            Log.d(TAG, e.getMessage());
        }
        return dexFiles;
    }

    public static List<String> getClassName(String packageName) {
        List<String> classNameList = new ArrayList<String>();
        List<DexFile> dexFiles = getAllDexFiles();
        for (DexFile df : dexFiles) {
            Enumeration<String> enumeration = df.entries();//获取df中的元素  这里包含了所有可执行的类名 该类名包含了包名+类名的方式
            while (enumeration.hasMoreElements()) {//遍历
                String className = enumeration.nextElement();
                if (className.contains("$")) {
                    continue;
                }
                if (className.contains(packageName)) {//在当前所有可执行的类里面查找包含有该包名的所有类
                    classNameList.add(className);
                }
            }
        }
        return classNameList;
    }

    private List<String> getAllTableName() {
        List<String> allTableName = new ArrayList<>();
        SQLiteDatabase database = DBHelper.get().getDb();
        Cursor cursor = database.rawQuery("select * " +
                "from sqlite_master ", null);
        while (cursor.moveToNext()) {
            String name = cursor.getString(cursor.getColumnIndex("name"));
            allTableName.add(name);
        }
        cursor.close();
        return allTableName;
    }

    /**
     * /**
     * 数据库表结构是否改变．
     *
     * @param tableInfo
     * @return 返回未修改的字段列表。
     */
    private List<String> getNoChangedColumn(SQLiteDatabase database, TableInfo tableInfo) {
        Cursor cursor = database.rawQuery("PRAGMA  table_info ( '" + tableInfo.getTableName() + "' )", null);
        Map<String, String> map = new HashMap<>();
        List<String> noChangedColumnList = new ArrayList<>(20);
        while (cursor.moveToNext()) {
            int index = cursor.getColumnIndex("name");
            String name = cursor.getString(index);
            String type = cursor.getString(cursor.getColumnIndex("type"));
            map.put(name, type);
        }
        //表的字段数量发生变化。（tableInfo.getColumnList()不包含主键，所以字段数量要加1）。
//        if (map.size() != tableInfo.getColumnList().size() + 1) {
//            return true;
//        }
        for (String column : tableInfo.getColumnList()) {
            //表中的实际类型。
            String typeInTable = map.get(column);
            //列不存在
            if (TextUtils.isEmpty(typeInTable)) {
                continue;
            }
            noChangedColumnList.add(column);
        }
        cursor.close();
        return noChangedColumnList;
    }

    public interface InitCallback {
        void onInitEnd();
    }

}
